package nirvana.core.bean.aspect;


import jakarta.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import nirvana.core.context.WebContext;
import nirvana.core.domains.ann.PreventBrush;
import nirvana.core.exceptions.BizException;
import nirvana.core.properties.PreventBrushProperties;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;


@Slf4j
@Aspect
@Component
@AllArgsConstructor
public class RequestLimitAspect {



    private final RedisTemplate<String,Integer> redisTemplate;


    private final PreventBrushProperties preventBrushProperties;


    private final HttpServletRequest request;


    @Pointcut("@annotation(nirvana.core.domains.ann.PreventBrush)")
    public void cut() {
    }


    /**
     * 接口防刷
     */
    @Before("cut()")
    public void interfaceBrush(JoinPoint point) {


        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();


        PreventBrush brushAnnotation = method.getAnnotation(PreventBrush.class);
        if (brushAnnotation==null){
            return;
        }


        String path = request.getRequestURI();


        // TODO: 2022-07-10 尝试使用cookieId
        Long userId = WebContext.getCurrentUserId();

        Object[] args = point.getArgs();
        Integer paramCode = null;
        if (args.length > 0) {
            Object param = args[0];
            paramCode = param.hashCode();
        }


        /**
         * 拼接redis的key
         *
         * key = 用户id+参数的哈希值
         */
        StringBuilder brushBuilder = new StringBuilder();
        brushBuilder.append("brush_").append(path)
                .append(userId)
                .append(paramCode);
        String brushKey = brushBuilder.toString();

        //获取防刷时间
        Integer brushTime = brushAnnotation.brushTime();
        if (null==brushTime){
            brushTime= preventBrushProperties.getBrushTime();
        }

        //获取最大数量
        Integer maxHits = brushAnnotation.maxHits();
        //如果注解里没有定义最大次数，则读取配置中的默认值
        if (null==maxHits){
            maxHits=preventBrushProperties.getMaxHits();
        }



//        获取短期内的请求记录
        //获取当前接口已请求次数
        Integer requestCount = redisTemplate.opsForValue().get(brushKey);
        ValueOperations<String, Integer> valueOperations = redisTemplate.opsForValue();

        if (requestCount == null) {
            requestCount =1;
            valueOperations.set(brushKey, requestCount, brushTime,TimeUnit.MILLISECONDS);
        } else {

            if (requestCount >= maxHits) {
                log.error("触发防刷 " + request.getRequestURI() + " " + request.getSession().getId());
                throw new BizException("您刷的太勤快了");
            }
            valueOperations.set(brushKey,++requestCount, brushTime,TimeUnit.MILLISECONDS);
        }
    }


}
